/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import React, { Component } from 'react';
import { View, StyleSheet, LayoutChangeEvent, LayoutRectangle, StyleProp, ViewStyle, Dimensions, LayoutAnimation } from 'react-native';
import Orientation, { specificOrientation, orientation } from 'react-native-orientation';
import { Fold } from '../utils/FoldUtils';
import { throttle } from '../utils/ThrottleUtils';

type Alignment = 'TopStart' | 'Top' | 'TopEnd' | 'Start' | 'Center' | 'End' | 'BottomStart' | 'Bottom' | 'BottomEnd';
type FOLD_STATUS = 'unknown' | 'expanded' | 'folded' | 'half-folded' | 'expanded-with-second-half-folded' | 'folded-with-second-expanded' | 'folded-with-second-half-folded' | 'half-folded-with-second-expanded' | 'half-folded-with-second-half-folded';
type HoverStatus = {
  foldStatus: FoldStatus;
  isHoverMode: boolean;
  appRotation: AppRotation;
};
type Layout = LayoutRectangle;
type AppRotationType = specificOrientation | 'LANDSCAPE_LEFT' | 'LANDSCAPE_RIGHT' | 'PORTRAIT_INVERTED';
let checkSizeTimer: number | NodeJS.Timeout | undefined;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    width: '100%',
    height: '100%',
    flexDirection: 'column',
  },
  fullSection: {
    flex: 1,
    width: '100%',
    marginTop: 0,
    opacity: 1,
  },
  upperSection: {
    flex: 1,
    overflow: 'hidden',
  },
  lowerSection: {
    flex: 1,
    overflow: 'hidden',
    marginTop: 0,
    opacity: 1,
  },
  foldIndicator: {
    height: 0,
  },
  groupContainer: {
    flex: 1,
    position: 'relative',
  },
});

const containerFullErrorSize = 1;

export interface FolderStackProps {
  upperItems?: string[];
  enableAnimation?: boolean;
  alignContent?: Alignment;
  autoHalfFold?: boolean;
  onFolderStateChange?: (status: { foldStatus: FoldStatus }) => void;
  onHoverStatusChange?: (status: HoverStatus) => void;
  children?: React.ReactNode;
}

export enum FoldStatus {
  FOLD_STATUS_UNKNOWN,
  FOLD_STATUS_EXPANDED,
  FOLD_STATUS_FOLDED,
  FOLD_STATUS_HALF_FOLDED,
}

export enum AppRotation {
  ROTATION_0,
  ROTATION_90,
  ROTATION_180,
  ROTATION_270,
}

interface FolderStackState {
  foldStatus: FOLD_STATUS;
  isHoverMode: boolean;
  childLayouts: Map<string, Layout>;
  creaseRegions?: Array<Array<number>>;
  childWidths: Map<string, number>;
  childHeights: Map<string, number>;
  orientation: orientation;
  containerFull: boolean;
  effectiveShouldFold: boolean;
  appRotation: AppRotationType;
}

export class FolderStack extends Component<FolderStackProps, FolderStackState> {
  static defaultProps = {
    enableAnimation: true,
    alignContent: 'Center' as Alignment,
    autoHalfFold: true,
  };

  constructor(props: FolderStackProps) {
    super(props);
    this.state = {
      foldStatus: Fold?.isFoldable() ? Fold?.getFoldStatus() : 'expanded',
      isHoverMode: false,
      orientation: 'UNKNOWN',
      childLayouts: new Map(),
      creaseRegions: [[]],
      childWidths: new Map(),
      childHeights: new Map(),
      containerFull: false,
      effectiveShouldFold: false,
      appRotation: 'UNKNOWN',
    };
  }

  componentDidMount(): void {
    this.setState({
      creaseRegions: Fold?.getFoldCreaseRegion(),
    });
    Orientation.getOrientation((err, ori): void => {
      if (err) {
        return;
      }
      this.setState({
        orientation: ori,
      });
    });
    Fold?.addFoldListener(e => this.handleFoldChange(e));
    Orientation.addOrientationListener(e => this.handleOrientation(e));
    Orientation.addSpecificOrientationListener(e => this.handleSpecificOrientationChange(e));
    if (this.props.autoHalfFold) {
      Orientation.unlockAllOrientations();
    }
  }

  componentWillUnmount(): void {
    Fold?.removeFoldListener();
    Orientation.removeOrientationListener(e => this.handleOrientation(e));
    Orientation.removeSpecificOrientationListener(e => this.handleSpecificOrientationChange(e));
    clearTimeout(checkSizeTimer);
  }

  componentDidUpdate(_prevProps: Readonly<FolderStackProps>, prevState: Readonly<FolderStackState>): void {
    const prevEffective = this.shouldAnimateLayout(prevState);
    const currentEffective = this.shouldAnimateLayout(this.state);
    if (prevEffective !== currentEffective) {
      this.throttledUpdateHoverStatus();
    }
    if (!this.props.enableAnimation) {
      return;
    }
    if (prevEffective !== currentEffective) {
      LayoutAnimation.configureNext({
        duration: 300,
        create: { type: 'linear', property: 'opacity' },
        update: { type: 'spring', springDamping: 0.8 },
        delete: { type: 'linear', property: 'opacity' },
      });
    }
  }

  private shouldAnimateLayout(state: FolderStackState): boolean {
    return state.foldStatus === 'half-folded' && this.shouldUseSplitViewInHover() && state.containerFull;
  }

  private handleFoldChange = (foldStatus: FOLD_STATUS): void => {
    this.setState(
      prev => ({
        foldStatus,
        isHoverMode: foldStatus === 'half-folded' ? true : prev.foldStatus === 'half-folded' ? false : prev.isHoverMode,
      }),
      () => {
        if (this.shouldUseSplitViewInHover()) {
          this.props.onFolderStateChange?.({
            foldStatus: this.mapFoldStatusToEnum(),
          });
        }
      },
    );
  };

  private handleOrientation = (orientation: orientation): void => {
    this.setState({ orientation: orientation });
  };

  private mapFoldStatusToEnum(): FoldStatus {
    let currentFoldStatus = FoldStatus.FOLD_STATUS_UNKNOWN;
    if (this.state.foldStatus === 'unknown') {
      currentFoldStatus = FoldStatus.FOLD_STATUS_UNKNOWN;
    } else if (this.state.foldStatus === 'expanded') {
      currentFoldStatus = FoldStatus.FOLD_STATUS_EXPANDED;
    } else if (this.state.foldStatus === 'folded') {
      currentFoldStatus = FoldStatus.FOLD_STATUS_FOLDED;
    } else if (this.state.foldStatus === 'half-folded') {
      currentFoldStatus = FoldStatus.FOLD_STATUS_HALF_FOLDED;
    }
    return currentFoldStatus;
  }

  private handleSpecificOrientationChange = (appRotation: specificOrientation): void => {
    this.setState({ appRotation });
  };

  private updateHoverStatus = (): void => {
    const { isHoverMode } = this.state;
    let currentAppRotation = AppRotation.ROTATION_0;
    if (this.state.appRotation === 'LANDSCAPE-LEFT' || this.state.appRotation === 'LANDSCAPE_LEFT') {
      currentAppRotation = AppRotation.ROTATION_270;
    } else if (this.state.appRotation === 'LANDSCAPE-RIGHT' || this.state.appRotation === 'LANDSCAPE_RIGHT') {
      currentAppRotation = AppRotation.ROTATION_90;
    } else if (this.state.appRotation === 'PORTRAIT') {
      currentAppRotation = AppRotation.ROTATION_0;
    } else if (this.state.appRotation === 'PORTRAITUPSIDEDOWN' || this.state.appRotation === 'PORTRAIT_INVERTED') {
      currentAppRotation = AppRotation.ROTATION_180;
    }
    this.props.onHoverStatusChange?.({
      foldStatus: this.mapFoldStatusToEnum(),
      isHoverMode,
      appRotation: currentAppRotation,
    });
    this.setState({
      creaseRegions: Fold?.getFoldCreaseRegion(),
    });
  };

  private throttledUpdateHoverStatus = throttle(this.updateHoverStatus.bind(this), 50);

  private handleChildLayout = (id: string, event: LayoutChangeEvent): void => {
    const { layout } = event.nativeEvent;
    this.setState(prev => ({
      childLayouts: new Map(prev.childLayouts).set(id, layout),
      childWidths: new Map(prev.childWidths).set(id, layout.width),
      childHeights: new Map(prev.childHeights).set(id, layout.height),
    }));
  };

  private getSafeAreaStyle(): ViewStyle | null {
    if (!this.state.creaseRegions?.length) {
      return null;
    }
    const mainCrease = this.state.creaseRegions[0]; // 取主折痕
    if (!mainCrease?.length) {
      return null;
    }
    return {
      height: mainCrease[3],
    };
  }

  private getAlignmentStyle(alignment: Alignment, id?: string): ViewStyle {
    const alignmentMap: Record<Alignment, ViewStyle> = {
      TopStart: { position: 'absolute', top: 0, left: 0 },
      Top: {
        position: 'absolute',
        top: 0,
        left: '50%',
        transform: id ? [{ translateX: -(this.state.childWidths.get(id) || 0) / 2 }] : undefined,
      },
      TopEnd: { position: 'absolute', top: 0, right: 0 },
      Start: {
        position: 'absolute',
        left: 0,
        top: '50%',
        transform: id ? [{ translateY: -(this.state.childHeights.get(id) || 0) / 2 }] : undefined,
      },
      Center: {
        position: 'absolute',
        top: '50%',
        left: '50%',
        transform: id ? [{ translateX: -(this.state.childWidths.get(id) || 0) / 2 }, { translateY: -(this.state.childHeights.get(id) || 0) / 2 }] : undefined,
      },
      End: {
        position: 'absolute',
        top: '50%',
        right: 0,
        transform: id ? [{ translateY: -(this.state.childHeights.get(id) || 0) / 2 }] : undefined,
      },
      BottomStart: { position: 'absolute', bottom: 0, left: 0 },
      Bottom: {
        position: 'absolute',
        bottom: 0,
        left: '50%',
        transform: id ? [{ translateX: -(this.state.childWidths.get(id) || 0) / 2 }] : undefined,
      },
      BottomEnd: { position: 'absolute', bottom: 0, right: 0 },
    };
    return alignmentMap[alignment];
  }

  private renderContentGroup(children: React.ReactNode[], _isUpper: boolean): JSX.Element {
    return (
      <View style={[styles.groupContainer]}>
        {React.Children.map(children, child => {
          if (!React.isValidElement(child)) {
            return null;
          }
          const validChild = child as React.ReactElement<{
            id: string;
            style?: StyleProp<ViewStyle>;
            onLayout?: (e: LayoutChangeEvent) => void;
          }>;
          return React.cloneElement(validChild, {
            style: [{ position: 'absolute' }, this.getAlignmentStyle(this.props.alignContent ?? 'Center', validChild.props.id), validChild.props.style],
            onLayout: (e: LayoutChangeEvent) => {
              validChild.props.onLayout?.(e);
              this.handleChildLayout(validChild.props.id, e);
            },
          });
        })}
      </View>
    );
  }

  private shouldUseSplitViewInHover(): boolean {
    // 如果竖屏状态下，折痕是水平折叠的（上下折叠），那么允许悬停
    const isPortraitOrientation = this.state.orientation === 'PORTRAIT';
    if (isPortraitOrientation && Fold.isHorizontalFold(isPortraitOrientation)) {
      return true;
    }
    // 如果横屏状态下，折痕是垂直折叠的（左右折叠），那么允许悬停
    else if (!isPortraitOrientation && !Fold.isHorizontalFold(isPortraitOrientation)) {
      return true;
    }
    else {
      return false;
    }
  }

  render(): JSX.Element {
    const { children, upperItems = [] } = this.props;
    const upperChildren: React.ReactNode[] = [];
    const lowerChildren: React.ReactNode[] = [];
    const shouldFold = this.state.foldStatus === 'half-folded' && this.shouldUseSplitViewInHover();
    const effectiveShouldFold = shouldFold && this.state.containerFull;

    React.Children.forEach(children, child => {
      if (React.isValidElement(child) && upperItems.includes(child.props.id)) {
        upperChildren.push(child);
      } else {
        lowerChildren.push(child);
      }
    });

    return (
      <View
        style={styles.container}
        onLayout={(e): void => {
          const { width, height } = e.nativeEvent.layout;
          let checkSize = (): void => {
            this.setState({
              containerFull:
                // width和Dimension.get('screen').width取值规则不同，导致全屏时两者取值存在误差，增加1px作为误差判断区间。
                Math.abs(width - Dimensions.get('screen').width) <= containerFullErrorSize &&
                Math.abs(height - Dimensions.get('screen').height) <= containerFullErrorSize,
            });
          };
          checkSize();
          checkSizeTimer = setTimeout(checkSize, 300);
        }}>
        {/* 折叠时显示的上部结构 */}
        {effectiveShouldFold && [
          <View style={styles.upperSection} key='upper'>
            {this.renderContentGroup(upperChildren, true)}
          </View>,
          <View style={[styles.foldIndicator, this.getSafeAreaStyle()]} key='indicator' />,
        ]}

        {/* 切换的内容容器 */}
        <View
          style={[
            effectiveShouldFold ? styles.lowerSection : styles.fullSection,
            effectiveShouldFold && {
              marginTop: this.state.effectiveShouldFold ? -50 : 0,
            },
          ]}>
          {this.renderContentGroup(effectiveShouldFold ? lowerChildren : React.Children.toArray(children), false)}
        </View>
      </View>
    );
  }
}
